<?php
/**
*
* @package YubiKey Login
* @version 1.0.3
* @copyright (c) 2010 Tim Schlueter
* @license http://opensource.org/licenses/gpl-license.php GNU Public License 
*
*/

/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
	exit;
}

if (!function_exists('yubikey_seen'))
{
	require_once 'Auth/Yubico.php';

	/* 
	 * Update YubiKey as seen.
	 */
	function yubikey_seen($deviceid)
	{
		global $db;

		$sql = 'UPDATE ' . YUBIKEY_TABLE . ' SET lastseen = ' . time() . ' WHERE deviceid = \'' . $db->sql_escape($deviceid) . '\'';
		$db->sql_query($sql);
	}

	/* 
	 * Increase number of login attempts.
	 */
	function yubikey_increase_login_attempts($deviceid, $user_id = 0)
	{
		global $db;

		if ($user_id == 0)
		{
			$sql = 'SELECT user_id FROM ' . YUBIKEY_TABLE . ' WHERE deviceid = \'' . $db->sql_escape($deviceid) . '\'';
			$result = $db->sql_query($sql);
			$row = $db->sql_fetchrow($result);
			$db->sql_freeresult($result);
			$user_id = (isset($row['user_id'])) ? $row['user_id'] : false;
		}

		$affected = 0;
		if ($user_id)
		{
			// increase the number of login attempts
			$sql = 'UPDATE ' . USERS_TABLE . ' SET user_login_attempts = user_login_attempts + 1 WHERE user_id = \'' . (int) $user_id . '\'';
			$db->sql_query($sql);
			$affected = $db->sql_affectedrows();
		}

		return $affected;
	}

	/* 
	 * Cleans validation serive url.
	 */
	function yubikey_clean_url($url) {
		return preg_replace("/^http[s]{0,1}:\/\//i", "", $url) ;
	}

	/*
	 * Check if OTP is valid YubiKey OTP.
	 */
	function yubikey_is_otp($otp) 
	{
		$ret = Auth_Yubico::parsePasswordOTP($otp);
		if (!$ret) {
			return;
		}
		return $ret['prefix'];
	}

	/* 
	 * Get YubiKey Device ID from OTP.
	 */
	function yubikey_get_deviceid($otp) 
	{
		return yubikey_is_otp($otp);
	}

	/*
	 * Retrieve YubiKey details
	 */
	function yubikey_get_details($deviceid) {
		global $db;

		$sql = 'SELECT * FROM ' . YUBIKEY_TABLE . ' WHERE deviceid = \'' . $db->sql_escape($deviceid) . '\'';
		$result = $db->sql_query($sql);
		$row = $db->sql_fetchrow($result);
		$db->sql_freeresult($result);

		if($row)
		{
			return $row;
		}
		return false;
	}

	/*
	 * Valdiate YubiKey OTP.
	 */
	function yubikey_validate_otp($otp)
	{
		global $config, $phpbb_root_path, $phpEx;

		if(!function_exists('curl_init')) 
		{
			return 'A required PHP library is missing.';
		}

		$api_id = (int) $config['yubico_api_id'];
		$api_key = $config['yubico_api_key'];
		$timeout = (int) $config['yubico_api_timeout'];
		$https = (int) $config['yubico_api_https'];

		$yubi = &new Auth_Yubico($api_id, $api_key, $https);
		if((int) $config['yubikey_val_ser_type'] == YUBIKEY_VAL_SERVICE_TYPE_CUSTOM)
		{
			for($i = 1; $i <= 5; $i++)
			{
				$url = $config['yubikey_val_ser_url' . $i];
				if($url != '')
				{
					$yubi->addURLpart(yubikey_clean_url($url));
				}
			}
		}
		$auth = $yubi->verify($otp, false, false, null, $timeout);
		//error_log('Yubikey OTP validation: Query details ::> ' . str_replace(" ", "\n", $yubi->getLastQuery() . " "));
		//error_log('YubiKey OTP validation: Debug output :: ' . $yubi->getLastResponse());
		if (PEAR::isError($auth))
		{
			//error_log('YubiKey OTP validation: Failed with message :: ' . $auth->getMessage());
			if($auth->getMessage() == 'REPLAYED_OTP')
			{
				$error_msg = 'LOGIN_ERROR_YUBIKEY_REPLAYED_OTP';
			}
			else
			{
				$error_msg = 'LOGIN_ERROR_YUBIKEY_NO_VALID_ANSWER';
			}
			return $error_msg;
		}
		return false;
	}

	function yubikey_login($username, $password, $otp)
	{
		global $config, $db;

		$yk_login_method = $config['yubikey_login_method'];
		if($otp)
		{
			// Check if OTP is valid YubiKey OTP
			$deviceid = yubikey_is_otp($otp);
			if(!$deviceid)
			{
				return array(
					'status'	=> LOGIN_ERROR_EXTERNAL_AUTH,
					'error_msg'	=> 'LOGIN_ERROR_YUBIKEY_BAD_OTP',
					'user_row'	=> array('user_id' => ANONYMOUS)
				);
			}

			// Mark YubiKey as seen
			yubikey_seen($deviceid);

			// Validate OTP
			$otp_val_status = yubikey_validate_otp($otp);
			if($otp_val_status)
			{
				yubikey_increase_login_attempts($deviceid);
				return array(
					'status'	=> LOGIN_ERROR_EXTERNAL_AUTH,
					'error_msg'	=> $otp_val_status,
					'user_row'	=> array('user_id' => ANONYMOUS)
				);
			}

			// Check if password field is supplied
			if(!$password && $yk_login_method != YUBIKEY_LOGIN_SCHEME_YK_ONLY)
			{
				return array(
					'status'	=> NO_PASSWORD_SUPPLIED,
					'error_msg'	=> 'NO_PASSWORD_SUPPLIED',
					'user_row'	=> array('user_id' => ANONYMOUS),
				);
			}

			// Check if username is specified
			if(!$username && $yk_login_method == YUBIKEY_LOGIN_SCHEME_UN_PWD_YK)
			{
				return array(
					'status'	=> LOGIN_ERROR_USERNAME,
					'error_msg'	=> 'LOGIN_ERROR_USERNAME',
					'user_row'	=> array('user_id' => ANONYMOUS),
				);
			}

			// Load the account associated with the specified YubiKey
			$sql  = ' SELECT ut.* FROM ' . USERS_TABLE . ' ut INNER JOIN ' . YUBIKEY_TABLE . ' yt';
			$sql .= ' ON ut.user_id = yt.user_id';
			$sql .= ' WHERE yt.deviceid = \'' . $db->sql_escape($deviceid) . '\'';
			$sql .= ' AND yt.status = \'' . YUBIKEY_ACTIVE . '\'';
			if($username) 
			{
				$sql .= ' AND username_clean = \'' . $db->sql_escape(utf8_clean_string($username)) . '\'';
			}

			$result = $db->sql_query($sql);
			$row = $db->sql_fetchrow($result);
			$db->sql_freeresult($result);

			if (!$row) 
			{
				return array(
					'status'	=> LOGIN_ERROR_EXTERNAL_AUTH,
					'error_msg'	=> 'LOGIN_ERROR_YUBIKEY_INACTIVE',
					'user_row'	=> array('user_id' => ANONYMOUS)
				);
			}
			else
			{
				return array(
					'status'	=> LOGIN_SUCCESS,
					'error_msg'	=> false,
					'user_row'	=> $row,
				);
			}
		}
		else
		{
			// OTP not provided
			if($yk_login_method == YUBIKEY_LOGIN_SCHEME_UN_OR_YK_PWD)
			{
				return false;
			}
			else if(!empty($username) && $yk_login_method == YUBIKEY_LOGIN_SCHEME_UN_PWD_YK) 
			{
				$sql  = 'SELECT COUNT(ut.user_id) user_exists, COUNT(yt.deviceid) ids_count ';
				$sql .= 'FROM ' . USERS_TABLE . ' ut LEFT JOIN ' . YUBIKEY_TABLE . ' yt ';
				$sql .= 'ON ut.user_id = yt.user_id ';
				$sql .= 'WHERE username_clean = \'' . $db->sql_escape(utf8_clean_string($username)) . '\'';

				$result = $db->sql_query($sql);
				$row = $db->sql_fetchrow($result);
				$db->sql_freeresult($result);

				if ($row && $row['user_exists'] > 0) 
				{
					if($config['yubikey_login_optional'] == 1)
					{
						// OTP field is optional if user account doesn't have an associated YubiKey.
						if($row['ids_count'] == 0)
						{
							return false;
						}
					}
				}
			}
			return array(
				'status'	=> LOGIN_ERROR_EXTERNAL_AUTH,
				'error_msg'	=> 'LOGIN_ERROR_YUBIKEY_REQUIRED',
				'user_row'	=> array('user_id' => ANONYMOUS),
			);
		}

		return false;
	}
}

?>